package com.oros.db;


import java.util.*;

import com.vaadin.data.*;
import com.vditl.core.*;

public class AERawItem implements Item,UniSortedFilteredItem {
	final static long serialVersionUID = -13232443259470L;
    private long idkey;
    private Object timestamp=null;
    private boolean modified = false,bNewRaw=false,isMorePrimaryKeys=false;
    private UniVector properties;
    private AETableContainer parent;
    private String primaryKey;
    private int primaryKeyNo;
    private int[] MorePrimaryKeys= null;
    private int generatedDataAmount;
    private static Integer emptyInteger = new Integer(0);
    private static Long emptyLong = new Long(0);
    private static String emptyString = "";
    public static int SHOW_INFO_WITH_TYPES=1;
	public AERawItem()
	{
        idkey=-1;
	}
	public AERawItem(AETableContainer table,String[] columnNames,Object[] columnData,Class<?>[] columnTypes,
			   boolean isNewRaw,String primaryKeyColumn,AEGeneratedData[] generatedData)
	{
		int n=columnNames.length;
		bNewRaw = isNewRaw;
		parent = table;
		primaryKey = primaryKeyColumn;
		properties = new UniVector(n);
		generatedDataAmount = generatedData==null?0:generatedData.length;
		int generatedDataBase = n - generatedDataAmount;
		for(int i=0;i<n;i++)
		{
			Object vl = columnData[i];
			Class<?> tp = columnTypes[i];
			String colnm = columnNames[i];
            int generatedDataIndex = i-generatedDataBase;
            AEColumnProperty property = new AEColumnProperty(this,vl,tp,
            		(generatedDataIndex>=0)&&(generatedData[generatedDataIndex] instanceof AEFieldFormatter)?(AEFieldFormatter)generatedData[generatedDataIndex]:null,i);
            properties.addKeyAndValue(colnm, property);
            if (colnm.equals(primaryKey))
            {
            	primaryKeyNo=i;
            	if (vl!=null)
            	{
			      if (vl instanceof Integer)
				   idkey = ((Integer)vl).longValue();
			     else if (vl instanceof Long)
				   idkey = ((Long)vl).longValue();
            	}
            }
		}
		if (generatedDataAmount>0)
		{
			
			for(int i=0;i<generatedDataAmount;i++)
			{
				n=i+generatedDataBase;
				((AEColumnProperty)properties.getValue(n)).setUncheckedValue(generatedData[i].generateCell(parent, this,
						n,AEGeneratedData.ACTION_INIT));
			}
		}
	}
	public boolean copyFromEntity(Object entity,VaadEngine engine)
	{
        boolean isNewEntity = true;
        if (engine.vdIsTimeStampSupported())
        {
        	Object newTimeStamp = engine.vdGetTimeStamp(entity);
            if (newTimeStamp==timestamp) isNewEntity=false;
            else 
            {
             if (newTimeStamp!=null && timestamp!=null) 
        	 {
        	    if (newTimeStamp.equals(timestamp))
        	      isNewEntity = false;	
        	 }
             timestamp = newTimeStamp;
            }
        }
        else
        {
        	throw new RuntimeException("Non-implemented feature");
        	//isNewEntity = !!;DEVELOP FOR ORACLE, MYSQL, POSTGRESQL
        }
        if (isNewEntity)
        {
         int n = properties.size()-generatedDataAmount;	
         for(int i=0;i<n;i++) 
         {
        	String key = properties.getKey(i); 
        	Object vl = engine.vdGetFieldValue(entity, key, i); 
        	if (vl==null && i==primaryKeyNo) continue;
        	
       	       AEColumnProperty prop =(AEColumnProperty)properties.getValue(i);
       	       prop.setUncheckedValue(vl);
        	   if (i==primaryKeyNo)
        		   {
        		      if (vl instanceof Integer)idkey = ((Integer)vl).longValue();
        		      else if (vl instanceof Long) idkey = ((Long)vl).longValue();
        		   }

        	
         }
         if (generatedDataAmount>0)
         {
        	 AEGeneratedData[] generatedData=parent.generatedData;
           for(int i=0;i<generatedDataAmount;i++)
           {
				((AEColumnProperty)properties.getValue(n+i)).setUncheckedValue(generatedData[i].generateCell(parent, this,
						n+i,AEGeneratedData.ACTION_COPY_FROM_ENTITY));
           }
         }
 		  modified = false;
        }
	  bNewRaw = false;
	  return isNewEntity;
	}
	public void updateGeneratedDataColumns()
	{
		AEGeneratedData[] generatedData=parent.generatedData;
		int totalGeneratedDataAmount = generatedData==null?0:generatedData.length;
		int newGeneratedDataAmount = totalGeneratedDataAmount - generatedDataAmount;
		int n=properties.size();
		if (newGeneratedDataAmount<=0) return;
        //delete can be implemented in the future
		for(int i=0;i<newGeneratedDataAmount;i++)
		{
			Object vl = parent.columnDefaults[i+n];
			Class<?> tp = parent.columnTypes[i+n];
			if (vl==null)
			{
                if (tp==Integer.class) vl=emptyInteger;
                else if (tp==Long.class) vl=emptyLong;
                else if (tp==String.class) vl=emptyString;
                else try
                {
                	vl = tp.newInstance();
                } catch(Exception e)
                {
                	
                }
			}
			AEGeneratedData dataFun = generatedData[i];
			String colnm = parent.columnNames[i+n];
            AEColumnProperty property = new AEColumnProperty(this,vl,tp,
            		dataFun instanceof AEFieldFormatter?(AEFieldFormatter)dataFun:null,i+n);
            
            properties.addKeyAndValue(colnm, property);
		}
		for(int i=0;i<newGeneratedDataAmount;i++)
		{
			((AEColumnProperty)properties.getValue(n+i)).setUncheckedValue(generatedData[i+generatedDataAmount].generateCell(
					parent, this,n+i,AEGeneratedData.ACTION_INIT));
		}
		generatedDataAmount=totalGeneratedDataAmount;
	}
	public void updateGeneratedDataValues()
	{
        if (generatedDataAmount>0)
        {
		  int n = properties.size()-generatedDataAmount;
       	  AEGeneratedData[] generatedData=parent.generatedData;
          for(int i=0;i<generatedDataAmount;i++)
          {
				((AEColumnProperty)properties.getValue(n+i)).setUncheckedValue(generatedData[i].generateCell(
						parent, this,n+i,AEGeneratedData.ACTION_MODIFICATION));
          }
        }
	}
	public void setModified(boolean modified)
	{
		this.modified = modified;
		if (modified)
		{
	        if (generatedDataAmount>0)
	        {
			  int n = properties.size()-generatedDataAmount;
	       	  AEGeneratedData[] generatedData=parent.generatedData;
	          for(int i=0;i<generatedDataAmount;i++)
	          {
					((AEColumnProperty)properties.getValue(n+i)).setUncheckedValue(generatedData[i].generateCell(
							parent, this,n+i,AEGeneratedData.ACTION_MODIFICATION));
	          }
	        }
			parent.itemNotifiesOfModification(this);
		}
	}
	public String getTypeAndDefaultInfo()
	{
		return parent.getTableTypeAndDefaultInfo();
	}
	public void makeDirty()
	{
		this.modified = true;
	}
	public boolean isModified()
	{
		return modified || bNewRaw;
	}
	public boolean isNew()
	{
		return bNewRaw;
	}
	public void setNew(boolean isNewRaw)
	{
		bNewRaw = isNewRaw;
	}
	public void copyPropertiesToEntity(Object entity,VaadEngine engine) throws Exception 
	{
		int n=properties.size()-generatedDataAmount;
         for(int i=0;i<n;i++)
        	 if (i!=primaryKeyNo)	
         {
        	 AEColumnProperty prop =(AEColumnProperty)properties.getValue(i);
        	 engine.vdSetFieldValue(entity,properties.getKey(i),i,prop.getValue());
         }
	}
	public String getColumnName(int colno)
	{
		return properties.getKey(colno);
	}
	public int getRecordableColumnAmount()
	{
		return properties.size()-generatedDataAmount;
	}
	public int getPrimaryColumnNumber()
	{
		return primaryKeyNo;
	}
	public AEColumnProperty getAEColumnProperty(int colno)
	{
		return (AEColumnProperty)properties.getValue(colno);
	}
	public Object getColumnValue(int colno)
	{
		return ((AEColumnProperty)properties.getValue(colno)).getValue();
	}
	public Object getPrimaryColumnValue()
	{
		return ((AEColumnProperty)properties.getValue(primaryKeyNo)).getValue();
	}
	@Override
	public boolean addItemProperty(Object id, Property property) {
        String sid = (String)id;
        AEColumnProperty dprop = (AEColumnProperty)properties.get(sid);
        if (dprop==null) return false;
        if (property!=dprop) dprop.setValue(property.getValue());
		if (sid.equals(primaryKey)) 
			{
                            Object val=property.getValue();
                            if (val instanceof Long)
			    idkey=((Long)val).longValue();
                            else
			    idkey=((Integer)val).intValue();
			}
		return true;
	}
	public long getIdKey() { return idkey; }
	@Override
	public boolean equals(Object obj) 
	{
	   if (obj==null) return false;
	   long objid;
	   if (obj instanceof Integer)
	   {
		   objid = ((Integer)obj).intValue();
	   }  else if (obj instanceof Long)
           {
                objid = ((Long)obj).longValue();
           }
	   else if (obj instanceof AERawItem)
		   objid = ((AERawItem)obj).getIdKey();
	   else return false;
	   if (objid!=idkey) return false;
	   if (isMorePrimaryKeys)
	   {
		   if (!(obj instanceof AERawItem)) return false;
		   AERawItem aeobj = (AERawItem)obj;
		   int n = MorePrimaryKeys.length;
		   for(int i=0;i<n;i++)
		   {
			   Object a1=getColumnValue(MorePrimaryKeys[i]);
			   Object a2=aeobj.getColumnValue(MorePrimaryKeys[i]);
               if (a1==null && a2==null) continue;
               if (a1==null || a2==null || !a1.equals(a2)) return false;
		   }
	   }
	   return true;
	}  		
	public boolean isExactCopy(AERawItem src)
	{
		   int n=properties.size();
		   for(int i=0;i<n;i++)
		   {
			   Object a1=getColumnValue(i);
			   Object a2=src.getColumnValue(i);
            if (a1==null && a2==null) continue;
            if (a1==null || a2==null || !a1.equals(a2)) return false;
		   }
         return true;		
	}
	public void addPrimaryKeyFields(int[] adds)
	{
		isMorePrimaryKeys = true;
		MorePrimaryKeys = adds;
	}
	@Override
	public int hashCode() {
		int hash=(int)(idkey^(idkey>>16));
		if (isMorePrimaryKeys)
		{
			   int n=MorePrimaryKeys.length;
			   for(int i=0;i<n;i++)
			   {
				   Object a1=getColumnValue(MorePrimaryKeys[i]);
				   if (a1!=null) hash =hash*31+a1.hashCode();
			   }			
		}
		return hash;
		}
	@Override
	public Property getItemProperty(Object id) {
		Object prop = properties.getValueByKey(id);
		if (prop==null) return null;
		if (prop instanceof AEColumnProperty)
		{
			AEColumnProperty property = (AEColumnProperty)prop;
			return property;
		}
		//AEHelper.logInfo("Illegal type:"+prop+" from "+id);
		return (Property) prop;
	}
	@Override
	public Collection<?> getItemPropertyIds() {

		return parent.getContainerPropertyIds();
	}
	@Override
	public boolean removeItemProperty(Object id)
			throws UnsupportedOperationException {
		throw new UnsupportedOperationException("Cannot remove item properties in AERawItem");
	}
	public void commit()
	{
		int n = properties.size()-generatedDataAmount;
		for(int i=0;i<n;i++)
		{
			((AEColumnProperty)properties.getValue(i)).commit();
		}
        if (generatedDataAmount>0)
        {
       	 AEGeneratedData[] generatedData=parent.generatedData;
          for(int i=0;i<generatedDataAmount;i++)
          {
				((AEColumnProperty)properties.getValue(n+i)).setUncheckedValue(generatedData[i].generateCell(parent, this,n+i
						,AEGeneratedData.ACTION_COMMIT));
          }
        }
		modified = false;
		bNewRaw = false;
	}
	public void commit(long commitTime)
	{
		timestamp = commitTime;
		commit();
	}
	public void rollback()
	{
		int n = properties.size()-generatedDataAmount;
		for(int i=0;i<n;i++)
		{
			((AEColumnProperty)properties.getValue(i)).rollback();
		}
        if (generatedDataAmount>0)
        {
       	 AEGeneratedData[] generatedData=parent.generatedData;
          for(int i=0;i<generatedDataAmount;i++)
          {
				((AEColumnProperty)properties.getValue(n+i)).setUncheckedValue(generatedData[i].generateCell(parent, 
						this,n+i,AEGeneratedData.ACTION_ROLLBACK));
          }
        }
		modified = false;
	}
    boolean filtered=true;int filteredPlace=-1;
   public boolean isFiltered() { return filtered || bNewRaw; }
   public void setFiltered(boolean filtered) { this.filtered = filtered; }
   public int getFilteredPlace() { return filteredPlace; }
   public void setFilteredPlace(int place) { filteredPlace = place; }

   @SuppressWarnings("unchecked")
@Override
   public int compareTo(Object o) {
	   AERawItem a = (AERawItem)o;
	   int[] cols = parent.sortedColumns;
	   int colsamnt = parent.sortedColumnAmount;
	   int k=0;
	   if (a.isNew())
	   {
		   if (!this.bNewRaw) k=-1;
	   }
	   else if (this.bNewRaw) k=1;
	   for(int col=0;(col<colsamnt)&&(k==0);col++)
	   {
		   int realcol = cols[col];
		   boolean descending = realcol<0;
		   if (descending) realcol = -1-realcol;
		   Object pa = a.getColumnValue(realcol);
		   Object pb = getColumnValue(realcol);
		   if (pa==null)
		   {
			   if (pb!=null) k=1;
			   continue;
		   }
		   if (pb==null) { k=-1;break;}
		   if (pb instanceof Comparable)
		   {
			   k =((Comparable<Object>)pb).compareTo(pa);
			   if (descending) k=-k;
		   }
	   }
	   if (k==0)
	   {
		   long aid=a.getIdKey();
		   if (idkey>aid) k=1;
		   else if (idkey<aid) k=-1;
	   }
	return k;
   }
   public boolean setPropertyOfItem(int colno,Object value)
   {
       AEColumnProperty property = (AEColumnProperty)properties.getValue(colno);
	   property.setValue(value);
       return true;	   
   }
   public void setPrimaryKey(long id)
   {
	   idkey =  id;
	   ((AEColumnProperty)properties.getValue(primaryKeyNo)).setUncheckedValue(idkey);
   }
   @Override
   public String toString()
   {
	   return Long.toString(idkey);
	   //return super.hashCode()+":"+getDetailedInfo(0);
   }
   public String getDetailedInfo(int options)
   {
	   StringBuilder sb = new StringBuilder();
	   boolean withTypes = (options & SHOW_INFO_WITH_TYPES)!=0;
	   sb.append(idkey);
	   sb.append(" Time:");
	   sb.append(timestamp);
	   sb.append("  ");
	   int n=properties.size();
	   sb.append(n);
	   sb.append(" columns(");
	   for(int i=0;i<n;i++)
	   {
		   sb.append((String)properties.getKey(i));
		   sb.append(": ");
		   Object val=((AEColumnProperty)properties.getValue(i)).getValue();
		   if (val==null)
		   {
			  sb.append("null"); 
		   }
		   else
		   {
		      sb.append(val);
		      if (withTypes)
		      {
		         sb.append("("+val.getClass().getCanonicalName()+")");
		      }
		   }
		   sb.append(", ");
	   }
	   sb.append(") ");
	   sb.append(filteredPlace);
	   return sb.toString();
   }
   public void printlnDetailedInfo(int options)
   {
	   System.out.println(getDetailedInfo(options));
   }
   public boolean filterOut(AEFilter filter)
   {
	   return filterOut(filter.getColumnNo(),filter.getColumnName(),filter.getType(),filter.getValue(),filter.filterOperator);
   }
   @SuppressWarnings({ "rawtypes", "unchecked" })
public boolean filterOut(int columnNr,String columnName,Class<?> type,Object etalon,AEFilter.FilterOperator operator)
   {
	   Object src;
	   if (columnNr>=0) src=getColumnValue(columnNr);
	   else if (columnName.equals("Time_Stamp")) src=timestamp;
	   else return false;
	   switch(operator)
	   {
	   case EQUAL:
		   return etalon.equals(src);
	   case NOT_EQUAL:
		   return !etalon.equals(src);
	   case IN:
		   Object[] pool = (Object[])etalon;
		   int n = pool.length;
		   for(int i=0;i<n;i++) if (pool[i].equals(src)) return true;
		   return false;
	   case GREATER_THAN:
		   if (etalon instanceof Comparable) return ((Comparable)etalon).compareTo(src)<0;
		   return (!etalon.equals(src));
	   case GREATER_THAN_OR_EQUAL:
		   if (etalon instanceof Comparable) return ((Comparable)etalon).compareTo(src)<=0;
		   return (etalon.equals(src));
	   case LESS_THAN:
		   if (etalon instanceof Comparable) return ((Comparable)etalon).compareTo(src)>0;
		   return (!etalon.equals(src));
	   case LESS_THAN_OR_EQUAL:
		   if (etalon instanceof Comparable) return ((Comparable)etalon).compareTo(src)>=0;
		   return (etalon.equals(src));
	   }
	   return false;
   }
}
